#include "dtsc.h"
#include "defines.h"
#include "bitfields.h"
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <fstream>

namespace DTSC {
  /// Default constructor for packets - sets a null pointer and invalid packet.
  Packet::Packet() {
    data = NULL;
    bufferLen = 0;
    dataLen = 0;
    master = false;
    version = DTSC_INVALID;
  }

  /// Copy constructor for packets, copies an existing packet with same noCopy flag as original.
  Packet::Packet(const Packet & rhs) {
    master = false;
    bufferLen = 0;
    data = NULL;
    if (rhs.data && rhs.dataLen){
      reInit(rhs.data, rhs.dataLen, !rhs.master);
    }else{
      null();
    }
  }

  /// Data constructor for packets, either references or copies a packet from raw data.
  Packet::Packet(const char * data_, unsigned int len, bool noCopy) {
    master = false;
    bufferLen = 0;
    data = NULL;
    reInit(data_, len, noCopy);
  }

  /// This destructor clears frees the data pointer if the packet was not a reference.
  Packet::~Packet() {
    if (master && data) {
      free(data);
    }
  }

  /// Copier for packets, copies an existing packet with same noCopy flag as original.
  /// If going from copy to noCopy, this will free the data pointer first.
  void Packet::operator = (const Packet & rhs) {
    if (master && !rhs.master) {
      null();
    }
    if (rhs && rhs.data && rhs.dataLen) {
      reInit(rhs.data, rhs.dataLen, !rhs.master);
    } else {
      null();
    }
  }

  /// Returns true if the packet is deemed valid, false otherwise.
  /// Valid packets have a length of at least 8, known header type, and length equal to the length set in the header.
  Packet::operator bool() const {
    if (!data) {
      DONTEVEN_MSG("No data");
      return false;
    }
    if (dataLen < 8) {
      VERYHIGH_MSG("Datalen < 8");
      return false;
    }
    if (version == DTSC_INVALID) {
      VERYHIGH_MSG("No valid version");
      return false;
    }
    if (ntohl(((int *)data)[1]) + 8 > dataLen) {
      VERYHIGH_MSG("Length mismatch");
      return false;
    }
    return true;
  }

  /// Returns the recognized packet type.
  /// This type is set by reInit and all constructors, and then just referenced from there on.
  packType Packet::getVersion() const {
    return version;
  }

  /// Resets this packet back to the same state as if it had just been freshly constructed.
  /// If needed, this frees the data pointer.
  void Packet::null() {
    if (master && data) {
      free(data);
    }
    master = false;
    data = NULL;
    bufferLen = 0;
    dataLen = 0;
    version = DTSC_INVALID;
  }

  /// Internally used resize function for when operating in copy mode and the internal buffer is too small.
  /// It will only resize up, never down.
  ///\param len The length th scale the buffer up to if necessary
  void Packet::resize(size_t len) {
    if (master && len > bufferLen) {
      char * tmp = (char *)realloc(data, len);
      if (tmp) {
        data = tmp;
        bufferLen = len;
      } else {
        FAIL_MSG("Out of memory on parsing a packet");
      }
    }
  }

  void Packet::reInit(Socket::Connection & src) {
    int sleepCount = 0;
    null();
    int toReceive = 0;
    while (src.connected()){
      if (!toReceive && src.Received().available(8)){
        if (src.Received().copy(2) != "DT"){
          WARN_MSG("Invalid DTSC Packet header encountered (%s)", src.Received().copy(4).c_str());
          break;
        }
        toReceive = Bit::btohl(src.Received().copy(8).data() + 4);
      }
      if (toReceive && src.Received().available(toReceive + 8)){
        std::string dataBuf = src.Received().remove(toReceive + 8);
        reInit(dataBuf.data(), dataBuf.size());
        return;
      }
      if(!src.spool()){
        if (sleepCount++ > 150){
          WARN_MSG("Waiting for packet on connection timed out");
          return;
        }
        Util::wait(100);
      }
    }
  }

  ///\brief Initializes a packet with new data
  ///\param data_ The new data for the packet
  ///\param len The length of the data pointed to by data_
  ///\param noCopy Determines whether to make a copy or not
  void Packet::reInit(const char * data_, unsigned int len, bool noCopy) {
    if (!data_) {
      WARN_MSG("ReInit received a null pointer with len %d, nulling", len);
      null();
      return;
    }
    if (!data_[0] && !data_[1] && !data_[2] && !data_[3]){
      null();
      return;
    }
    if (data_[0] != 'D' || data_[1] != 'T') {
      unsigned int twlen = len;
      if (twlen > 20){twlen = 20;}
      HIGH_MSG("ReInit received a pointer that didn't start with 'DT' but with %s (%u) - data corruption?", JSON::Value(std::string(data_, twlen)).toString().c_str(), len);
      null();
      return;
    }
    if (len <= 0) {
      len = ntohl(((int *)data_)[1]) + 8;
    }
    //clear any existing controlled contents
    if (master && noCopy) {
      null();
    }
    //set control flag to !noCopy
    master = !noCopy;
    //either copy the data, or only the pointer, depending on flag
    if (noCopy) {
      data = (char *)data_;
    } else {
      resize(len);
      memcpy(data, data_, len);
    }
    //check header type and store packet length
    dataLen = len;
    version = DTSC_INVALID;
    if (len < 4) {
      FAIL_MSG("ReInit received a packet with size < 4");
      return;
    }
    if (!memcmp(data, Magic_Packet2, 4)) {
      version = DTSC_V2;
    }
    if (!memcmp(data, Magic_Packet, 4)) {
      version = DTSC_V1;
    }
    if (!memcmp(data, Magic_Header, 4)) {
      version = DTSC_HEAD;
    }
    if (!memcmp(data, Magic_Command, 4)) {
      version = DTCM;
    }
    if (version == DTSC_INVALID){
      FAIL_MSG("ReInit received a packet with invalid header");
    }
  }
  
  /// Re-initializes this Packet to contain a generic DTSC packet with the given data fields.
  /// When given a NULL pointer, the data is reserved and memset to 0
  /// If given a NULL pointer and a zero size, an empty packet is created.
  void Packet::genericFill(long long packTime, long long packOffset, long long packTrack, const char * packData, long long packDataSize, uint64_t packBytePos, bool isKeyframe, int64_t bootMsOffset){
    null();
    master = true;
    //time and trackID are part of the 20-byte header.
    //the container object adds 4 bytes (plus 2+namelen for each content, see below)
    //offset, if non-zero, adds 9 bytes (integer type) and 8 bytes (2+namelen)
    //bpos, if >= 0, adds 9 bytes (integer type) and 6 bytes (2+namelen)
    //keyframe, if true, adds 9 bytes (integer type) and 10 bytes (2+namelen)
    //bootmsoffset, if != 0, adds 9 bytes (integer type) and 5 bytes (2+namelen)
    //data adds packDataSize+5 bytes (string type) and 6 bytes (2+namelen)
    if (packData && packDataSize < 1){
      FAIL_MSG("Attempted to fill a packet with %lli bytes!", packDataSize);
      return;
    }
    unsigned int sendLen = 24 + (packOffset?17:0) + (packBytePos?15:0) + (isKeyframe?19:0) + (bootMsOffset?14:0) + packDataSize+11;
    resize(sendLen);
    //set internal variables
    version = DTSC_V2;
    dataLen = sendLen;
    //write the first 20 bytes
    memcpy(data, "DTP2", 4);
    unsigned int tmpLong = htonl(sendLen - 8);
    memcpy(data+4, (char *)&tmpLong, 4);
    tmpLong = htonl(packTrack);
    memcpy(data+8, (char *)&tmpLong, 4);
    tmpLong = htonl((int)(packTime >> 32));
    memcpy(data+12, (char *)&tmpLong, 4);
    tmpLong = htonl((int)(packTime & 0xFFFFFFFF));
    memcpy(data+16, (char *)&tmpLong, 4);
    data[20] = 0xE0;//start container object
    unsigned int offset = 21;
    if (packOffset){
      memcpy(data+offset, "\000\006offset\001", 9);
      tmpLong = htonl((int)(packOffset >> 32));
      memcpy(data+offset+9, (char *)&tmpLong, 4);
      tmpLong = htonl((int)(packOffset & 0xFFFFFFFF));
      memcpy(data+offset+13, (char *)&tmpLong, 4);
      offset += 17;
    }
    if (packBytePos){
      memcpy(data+offset, "\000\004bpos\001", 7);
      tmpLong = htonl((int)(packBytePos >> 32));
      memcpy(data+offset+7, (char *)&tmpLong, 4);
      tmpLong = htonl((int)(packBytePos & 0xFFFFFFFF));
      memcpy(data+offset+11, (char *)&tmpLong, 4);
      offset += 15;
    }
    if (isKeyframe){
      memcpy(data+offset, "\000\010keyframe\001\000\000\000\000\000\000\000\001", 19);
      offset += 19;
    }
    if (bootMsOffset){
      memcpy(data+offset, "\000\003bmo\001", 6);
      tmpLong = htonl((int)(bootMsOffset >> 32));
      memcpy(data+offset+6, (char *)&tmpLong, 4);
      tmpLong = htonl((int)(bootMsOffset & 0xFFFFFFFF));
      memcpy(data+offset+10, (char *)&tmpLong, 4);
      offset += 14;
    }
    memcpy(data+offset, "\000\004data\002", 7);
    tmpLong = htonl(packDataSize);
    memcpy(data+offset+7, (char *)&tmpLong, 4);
    if (packData){
      memcpy(data+offset+11, packData, packDataSize);
    }else{
      memset(data+offset+11, 0, packDataSize);
    }
    //finish container with 0x0000EE
    memcpy(data+offset+11+packDataSize, "\000\000\356", 3);
  }

  ///sets the keyframe byte.
  void Packet::setKeyFrame(bool kf){
    uint32_t offset = 23;
    while (data[offset] != 'd' && data[offset] != 'k' && data[offset] != 'K'){
      switch (data[offset]){
        case 'o': offset += 17; break;
        case 'b': offset += 15; break;
        default:
          FAIL_MSG("Unknown field: %c", data[offset]);
      }
    }

    if(data[offset] == 'k' || data[offset] == 'K'){
      data[offset] = (kf?'k':'K');
      data[offset+16] = (kf?1:0);
    }else{
      ERROR_MSG("Could not set keyframe - field not found!");
    }
  }

  void Packet::appendData(const char * appendData, uint32_t appendLen){
    resize(dataLen + appendLen);
    memcpy(data + dataLen-3, appendData, appendLen);
    memcpy(data + dataLen-3 + appendLen, "\000\000\356", 3);  //end container
    dataLen += appendLen;
    Bit::htobl(data+4, Bit::btohl(data +4)+appendLen);
    uint32_t offset = getDataStringLenOffset();
    Bit::htobl(data+offset, Bit::btohl(data+offset)+appendLen);
  }

  void Packet::appendNal(const char * appendData, uint32_t appendLen){
    if(appendLen ==0){
      return;
    }

    resize(dataLen + appendLen +4);
    Bit::htobl(data+dataLen -3, appendLen);
    memcpy(data + dataLen-3+4, appendData, appendLen);
    memcpy(data + dataLen-3+4 + appendLen, "\000\000\356", 3);  //end container
    dataLen += appendLen +4;
    Bit::htobl(data+4, Bit::btohl(data +4)+appendLen+4);
    uint32_t offset = getDataStringLenOffset();
    Bit::htobl(data+offset, Bit::btohl(data+offset)+appendLen+4);

    prevNalSize = appendLen;
  }

  void Packet::upgradeNal(const char * appendData, uint32_t appendLen){
    if(appendLen ==0){
      return;
    }
    uint64_t sizeOffset = dataLen - 3 - 4 - prevNalSize;
    if (Bit::btohl(data + sizeOffset) != prevNalSize){
      FAIL_MSG("PrevNalSize state not correct");
      return;
    }
    resize(dataLen + appendLen);//Not + 4 as size bytes have already been written here.
    Bit::htobl(data+sizeOffset, prevNalSize + appendLen);
    prevNalSize += appendLen;
    memcpy(data + dataLen - 3, appendData, appendLen);
    memcpy(data + dataLen - 3 + appendLen, "\000\000\356", 3);  //end container
    dataLen += appendLen;
    Bit::htobl(data+4, Bit::btohl(data +4)+appendLen);
    uint32_t offset = getDataStringLenOffset();
    Bit::htobl(data+offset, Bit::btohl(data+offset)+appendLen);
  }

  size_t Packet::getDataStringLen(){
    return Bit::btohl(data+getDataStringLenOffset());
  }

  ///Method can only be used when using internal functions to build the data.
  size_t Packet::getDataStringLenOffset(){
    size_t offset = 23;
    while (data[offset] != 'd'){
      switch (data[offset]){
        case 'o': offset += 17; break;
        case 'b': offset += 15; break;
        case 'k': offset += 19; break;
        case 'K': offset += 19; break;
        default:
          FAIL_MSG("Unknown field: %c", data[offset]);
          return -1;
      }
    }
    return offset +5;
  }


  /// Helper function for skipping over whole DTSC parts
  static char * skipDTSC(char * p, char * max) {
    if (p + 1 >= max || p[0] == 0x00) {
      return 0;//out of packet! 1 == error
    }
    if (p[0] == DTSC_INT) {
      //int, skip 9 bytes to next value
      return p + 9;
    }
    if (p[0] == DTSC_STR) {
      if (p + 4 >= max) {
        return 0;//out of packet!
      }
      return p + 5 + Bit::btohl(p+1);
    }
    if (p[0] == DTSC_OBJ || p[0] == DTSC_CON) {
      p++;
      //object, scan contents
      while (p < max && p[0] + p[1] != 0) { //while not encountering 0x0000 (we assume 0x0000EE)
        if (p + 2 >= max) {
          return 0;//out of packet!
        }
        p += 2 + Bit::btohs(p);//skip size
        //otherwise, search through the contents, if needed, and continue
        p = skipDTSC(p, max);
        if (!p) {
          return 0;
        }
      }
      return p + 3;
    }
    if (p[0] == DTSC_ARR) {
      p++;
      //array, scan contents
      while (p < max && p[0] + p[1] != 0) { //while not encountering 0x0000 (we assume 0x0000EE)
        //search through contents...
        p = skipDTSC(p, max);
        if (!p) {
          return 0;
        }
      }
      return p + 3; //skip end marker
    }
    return 0;//out of packet! 1 == error
  }

  ///\brief Retrieves a single parameter as a string
  ///\param identifier The name of the parameter
  ///\param result A location on which the string will be returned
  ///\param len An integer in which the length of the string will be returned
  void Packet::getString(const char * identifier, char *& result, size_t & len) const {
    getScan().getMember(identifier).getString(result, len);
  }

  ///\brief Retrieves a single parameter as a string
  ///\param identifier The name of the parameter
  ///\param result The string in which to store the result
  void Packet::getString(const char * identifier, std::string & result) const {
    result = getScan().getMember(identifier).asString();
  }

  ///\brief Retrieves a single parameter as an integer
  ///\param identifier The name of the parameter
  ///\param result The result is stored in this integer
  void Packet::getInt(const char * identifier, uint64_t & result) const {
    result = getScan().getMember(identifier).asInt();
  }

  ///\brief Retrieves a single parameter as an integer
  ///\param identifier The name of the parameter
  ///\result The requested parameter as an integer
  uint64_t Packet::getInt(const char * identifier) const {
    uint64_t result;
    getInt(identifier, result);
    return result;
  }

  ///\brief Retrieves a single parameter as a boolean
  ///\param identifier The name of the parameter
  ///\param result The result is stored in this boolean
  void Packet::getFlag(const char * identifier, bool & result) const {
    uint64_t result_;
    getInt(identifier, result_);
    result = result_;
  }

  ///\brief Retrieves a single parameter as a boolean
  ///\param identifier The name of the parameter
  ///\result The requested parameter as a boolean
  bool Packet::getFlag(const char * identifier) const {
    bool result;
    getFlag(identifier, result);
    return result;
  }

  ///\brief Checks whether a parameter exists
  ///\param identifier The name of the parameter
  ///\result Whether the parameter exists or not
  bool Packet::hasMember(const char * identifier) const {
    return getScan().getMember(identifier).getType() > 0;
  }

  ///\brief Returns the timestamp of the packet.
  ///\return The timestamp of this packet.
  uint64_t Packet::getTime() const {
    if (version != DTSC_V2) {
      if (!data) {
        return 0;
      }
      return getInt("time");
    }
    return Bit::btohll(data + 12);
  }

  void Packet::setTime(uint64_t _time) {
    if (!master){
      INFO_MSG("Can't set the time for this packet, as it is not master.");
      return;
    }
    Bit::htobll(data + 12, _time);
  }

  ///\brief Returns the track id of the packet.
  ///\return The track id of this packet.
  size_t Packet::getTrackId() const {
    if (version != DTSC_V2) {
      return getInt("trackid");
    }
    return Bit::btohl(data+8);
  }

  ///\brief Returns a pointer to the payload of this packet.
  ///\return A pointer to the payload of this packet.
  char * Packet::getData() const {
    return data;
  }

  ///\brief Returns the size of this packet.
  ///\return The size of this packet.
  size_t Packet::getDataLen() const {
    return dataLen;
  }

  ///\brief Returns the size of the payload of this packet.
  ///\return The size of the payload of this packet.
  size_t Packet::getPayloadLen() const {
    if (version == DTSC_V2) {
      return dataLen - 20;
    } else {
      return dataLen - 8;
    }
  }

  /// Returns a DTSC::Scan instance to the contents of this packet.
  /// May return an invalid instance if this packet is invalid.
  Scan Packet::getScan() const {
    if (!*this || !getDataLen() || !getPayloadLen() || getDataLen() <= getPayloadLen()){
      return Scan();
    }
    return Scan(data + (getDataLen() - getPayloadLen()), getPayloadLen());
  }

  ///\brief Converts the packet into a JSON value
  ///\return A JSON::Value representation of this packet.
  JSON::Value Packet::toJSON() const {
    JSON::Value result;
    uint32_t i = 8;
    if (getVersion() == DTSC_V1) {
      JSON::fromDTMI(data, dataLen, i, result);
    }
    if (getVersion() == DTSC_V2) {
      JSON::fromDTMI2(data, dataLen, i, result);
    }
    return result;
  }

  std::string Packet::toSummary() const {
    std::stringstream out;
    char * res = 0;
    size_t len = 0;
    getString("data", res, len);
    out << getTrackId() << "@" << getTime() << ": " << len << " bytes";
    if (hasMember("keyframe")){
      out << " (keyframe)";
    }
    return out.str();
  }

  /// Create an invalid DTSC::Scan object by default.
  Scan::Scan() {
    p = 0;
    len = 0;
  }


  /// Create a DTSC::Scan object from memory pointer.
  Scan::Scan(char * pointer, size_t length) {
    p = pointer;
    len = length;
  }

  /// Returns whether the DTSC::Scan object contains valid data.
  Scan::operator bool() const {
    return (p && len);
  }

  /// Returns an object representing the named indice of this object.
  /// Returns an invalid object if this indice doesn't exist or this isn't an object type.
  Scan Scan::getMember(const std::string & indice) const {
    return getMember(indice.data(), indice.size());
  }

  /// Returns an object representing the named indice of this object.
  /// Returns an invalid object if this indice doesn't exist or this isn't an object type.
  Scan Scan::getMember(const char * indice, const size_t ind_len) const {
    if (getType() != DTSC_OBJ && getType() != DTSC_CON) {
      return Scan();
    }
    char * i = p + 1;
    //object, scan contents
    while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
      if (i + 2 >= p + len) {
        return Scan();//out of packet!
      }
      uint16_t strlen = Bit::btohs(i);
      i += 2;
      if (ind_len == strlen && strncmp(indice, i, strlen) == 0) {
        return Scan(i + strlen, len - (i - p));
      }
      i = skipDTSC(i + strlen, p + len);
      if (!i) {
        return Scan();
      }
    }
    return Scan();
  }

  /// Returns an object representing the named indice of this object.
  /// Returns an invalid object if this indice doesn't exist or this isn't an object type.
  bool Scan::hasMember(const std::string & indice) const{
    return getMember(indice.data(), indice.size());
  }

  /// Returns whether an object representing the named indice of this object exists.
  /// Returns false if this indice doesn't exist or this isn't an object type.
  bool Scan::hasMember(const char * indice, const size_t ind_len) const {
    return getMember(indice, ind_len);
  }

  /// Returns an object representing the named indice of this object.
  /// Returns an invalid object if this indice doesn't exist or this isn't an object type.
  Scan Scan::getMember(const char * indice) const {
    return getMember(indice, strlen(indice));
  }

  /// Returns the amount of indices if an array, the amount of members if an object, or zero otherwise.
  size_t Scan::getSize() const {
    uint32_t arr_indice = 0;
    if (getType() == DTSC_ARR) {
      char * i = p + 1;
      //array, scan contents
      while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
        //search through contents...
        arr_indice++;
        i = skipDTSC(i, p + len);
        if (!i) {
          break;
        }
      }
    }
    if (getType() == DTSC_OBJ || getType() == DTSC_CON) {
      char * i = p + 1;
      //object, scan contents
      while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
        if (i + 2 >= p + len) {
          return Scan();//out of packet!
        }
        uint16_t strlen = Bit::btohs(i);
        i += 2;
        arr_indice++;
        i = skipDTSC(i + strlen, p + len);
        if (!i) {
          break;
        }
      }
    }
    return arr_indice;
  }

  /// Returns an object representing the num-th indice of this array.
  /// If not an array but an object, it returns the num-th member, instead.
  /// Returns an invalid object if this indice doesn't exist or this isn't an array or object type.
  Scan Scan::getIndice(size_t num) const {
    if (getType() == DTSC_ARR) {
      char * i = p + 1;
      unsigned int arr_indice = 0;
      //array, scan contents
      while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
        //search through contents...
        if (arr_indice == num) {
          return Scan(i, len - (i - p));
        } else {
          arr_indice++;
          i = skipDTSC(i, p + len);
          if (!i) {
            return Scan();
          }
        }
      }
    }
    if (getType() == DTSC_OBJ || getType() == DTSC_CON) {
      char * i = p + 1;
      unsigned int arr_indice = 0;
      //object, scan contents
      while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
        if (i + 2 >= p + len) {
          return Scan();//out of packet!
        }
        unsigned int strlen = Bit::btohs(i);
        i += 2;
        if (arr_indice == num) {
          return Scan(i + strlen, len - (i - p));
        } else {
          arr_indice++;
          i = skipDTSC(i + strlen, p + len);
          if (!i) {
            return Scan();
          }
        }
      }
    }
    return Scan();
  }

  /// Returns the name of the num-th member of this object.
  /// Returns an empty string on error or when not an object.
  std::string Scan::getIndiceName(size_t num) const {
    if (getType() == DTSC_OBJ || getType() == DTSC_CON) {
      char * i = p + 1;
      unsigned int arr_indice = 0;
      //object, scan contents
      while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
        if (i + 2 >= p + len) {
          return "";//out of packet!
        }
        unsigned int strlen = Bit::btohs(i);
        i += 2;
        if (arr_indice == num) {
          return std::string(i, strlen);
        } else {
          arr_indice++;
          i = skipDTSC(i + strlen, p + len);
          if (!i) {
            return "";
          }
        }
      }
    }
    return "";
  }
  
  /// Returns the first byte of this DTSC value, or 0 on error.
  char Scan::getType() const {
    if (!p) {
      return 0;
    }
    return p[0];
  }

  /// Returns the boolean value of this DTSC value.
  /// Numbers are compared to 0.
  /// Strings are checked for non-zero length.
  /// Objects and arrays are checked for content.
  /// Returns false on error or in other cases.
  bool Scan::asBool() const {
    switch (getType()) {
      case DTSC_STR:
        return (p[1] | p[2] | p[3] | p[4]);
      case DTSC_INT:
        return (asInt() != 0);
      case DTSC_OBJ:
      case DTSC_CON:
      case DTSC_ARR:
        return (p[1] | p[2]);
      default:
        return false;
    }
  }

  /// Returns the long long value of this DTSC number value.
  /// Will convert string values to numbers, taking octal and hexadecimal types into account.
  /// Illegal or invalid values return 0.
  int64_t Scan::asInt() const {
    switch (getType()) {
      case DTSC_INT:
        return Bit::btohll(p+1);
      case DTSC_STR:
        char * str;
        size_t strlen;
        getString(str, strlen);
        if (!strlen) {
          return 0;
        }
        return strtoll(str, 0, 0);
      default:
        return 0;
    }
  }

  /// Returns the string value of this DTSC string value.
  /// Uses getString internally, if a string.
  /// Converts integer values to strings.
  /// Returns an empty string on error.
  std::string Scan::asString() const {
    switch (getType()) {
      case DTSC_INT:{
        std::stringstream st;
        st << asInt();
        return st.str();
      }
      break;
      case DTSC_STR:{
        char * str;
        size_t strlen;
        getString(str, strlen);
        return std::string(str, strlen);
      }
      break;
    }
    return "";
  }

  /// Sets result to a pointer to the string, and strlen to the length of it.
  /// Sets both to zero if this isn't a DTSC string value.
  /// Attempts absolutely no conversion.
  void Scan::getString(char *& result, size_t & strlen) const {
    if (getType() == DTSC_STR){
        result = p + 5;
        strlen = Bit::btohl(p+1);
        return;
    }
    result = 0;
    strlen = 0;
  }

  /// Returns the DTSC scan object as a JSON value
  /// Returns an empty object on error.
  JSON::Value Scan::asJSON() const {
    JSON::Value result;
    unsigned int i = 0;
    JSON::fromDTMI(p, len, i, result);
    return result;
  }

  /// \todo Move this function to some generic area. Duplicate from json.cpp
  static inline char hex2c(char c) {
    if (c < 10) {
      return '0' + c;
    }
    if (c < 16) {
      return 'A' + (c - 10);
    }
    return '0';
  }

  /// \todo Move this function to some generic area. Duplicate from json.cpp
  static std::string string_escape(const std::string val) {
    std::stringstream out;
    out << "\"";
    for (size_t i = 0; i < val.size(); ++i) {
      switch (val.data()[i]) {
        case '"':
          out << "\\\"";
          break;
        case '\\':
          out << "\\\\";
          break;
        case '\n':
          out << "\\n";
          break;
        case '\b':
          out << "\\b";
          break;
        case '\f':
          out << "\\f";
          break;
        case '\r':
          out << "\\r";
          break;
        case '\t':
          out << "\\t";
          break;
        default:
          if (val.data()[i] < 32 || val.data()[i] > 126) {
            out << "\\u00";
            out << hex2c((val.data()[i] >> 4) & 0xf);
            out << hex2c(val.data()[i] & 0xf);
          } else {
            out << val.data()[i];
          }
          break;
      }
    }
    out << "\"";
    return out.str();
  }

  std::string Scan::toPrettyString(size_t indent) const {
    switch (getType()) {
      case DTSC_STR: {
          uint32_t strlen = Bit::btohl(p+1);
          if (strlen > 250) {
            std::stringstream ret;
            ret << "\"" << strlen << " bytes of data\"";
            return ret.str();
          }
          return string_escape(asString());
        }
      case DTSC_INT: {
          std::stringstream ret;
          ret << asInt();
          return ret.str();
        }
      case DTSC_OBJ:
      case DTSC_CON: {
          std::stringstream ret;
          ret << "{" << std::endl;
          indent += 2;
          char * i = p + 1;
          bool first = true;
          //object, scan contents
          while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
            if (i + 2 >= p + len) {
              indent -= 2;
              ret << std::string(indent, ' ') << "} //walked out of object here";
              return ret.str();
            }
            if (!first){
              ret << "," << std::endl;
            }
            first = false;
            uint16_t strlen = Bit::btohs(i);
            i += 2;
            ret << std::string(indent, ' ') << "\"" << std::string(i, strlen) << "\": " << Scan(i + strlen, len - (i - p)).toPrettyString(indent);
            i = skipDTSC(i + strlen, p + len);
            if (!i) {
              indent -= 2;
              ret << std::string(indent, ' ') << "} //could not locate next object";
              return ret.str();
            }
          }
          indent -= 2;
          ret << std::endl << std::string(indent, ' ') << "}";
          return ret.str();
        }
      case DTSC_ARR: {
          std::stringstream ret;
          ret << "[" << std::endl;
          indent += 2;
          Scan tmpScan;
          unsigned int i = 0;
          bool first = true;
          do {
            tmpScan = getIndice(i++);
            if (tmpScan.getType()) {
              if (!first){
                ret << "," << std::endl;
              }
              first = false;
              ret << std::string(indent, ' ') << tmpScan.toPrettyString(indent);
            }
          } while (tmpScan.getType());
          indent -= 2;
          ret << std::endl << std::string(indent, ' ') << "]";
          return ret.str();
        }
      default:
        return "Error";
    }
  }



  ///\brief Returns the payloadsize of a part
  uint32_t Part::getSize() {
    return Bit::btoh24(data);
  }

  ///\brief Sets the payloadsize of a part
  void Part::setSize(uint32_t newSize) {
    Bit::htob24(data, newSize);
  }

  ///\brief Returns the duration of a part
  uint32_t Part::getDuration() {
    return Bit::btoh24(data + 3);
  }

  ///\brief Sets the duration of a part
  void Part::setDuration(uint32_t newDuration) {
    Bit::htob24(data + 3, newDuration);
  }

  ///\brief returns the offset of a part
  ///Assumes the offset is actually negative if bit 0x800000 is set.
  uint32_t Part::getOffset() {
    uint32_t ret = Bit::btoh24(data + 6);
    if (ret & 0x800000){
      return ret | 0xff000000ul;
    }else{
      return ret;
    }
  }

  ///\brief Sets the offset of a part
  void Part::setOffset(uint32_t newOffset) {
    Bit::htob24(data + 6, newOffset);
  }

  ///\brief Returns the data of a part
  char * Part::getData() {
    return data;
  }

  ///\brief Converts a part to a human readable string
  ///\param str The stringstream to append to
  ///\param indent the amount of indentation needed
  void Part::toPrettyString(std::ostream & str, int indent) {
    str << std::string(indent, ' ') << "Part: Size(" << getSize() << "), Dur(" << getDuration() << "), Offset(" << getOffset() << ")" << std::endl;
  }

  ///\brief Returns the byteposition of a keyframe
  unsigned long long Key::getBpos() {
    return Bit::btohll(data);
  }

  void Key::setBpos(unsigned long long newBpos) {
    Bit::htobll(data, newBpos);
  }

  unsigned long Key::getLength() {
    return Bit::btoh24(data+8);
  }

  void Key::setLength(unsigned long newLength) {
    Bit::htob24(data+8, newLength);
  }

  ///\brief Returns the number of a keyframe
  unsigned long Key::getNumber() {
    return Bit::btohl(data + 11);
  }

  ///\brief Sets the number of a keyframe
  void Key::setNumber(unsigned long newNumber) {
    Bit::htobl(data + 11, newNumber);
  }

  ///\brief Returns the number of parts of a keyframe
  unsigned short Key::getParts() {
    return Bit::btohs(data + 15);
  }

  ///\brief Sets the number of parts of a keyframe
  void Key::setParts(unsigned short newParts) {
    Bit::htobs(data + 15, newParts);
  }

  ///\brief Returns the timestamp of a keyframe
  unsigned long long Key::getTime() {
    return Bit::btohll(data + 17);
  }

  ///\brief Sets the timestamp of a keyframe
  void Key::setTime(unsigned long long newTime) {
    Bit::htobll(data + 17, newTime);
  }

  ///\brief Returns the data of this keyframe struct
  char * Key::getData() {
    return data;
  }

  ///\brief Converts a keyframe to a human readable string
  ///\param str The stringstream to append to
  ///\param indent the amount of indentation needed
  void Key::toPrettyString(std::ostream & str, int indent) {
    str << std::string(indent, ' ') << "Key " << getNumber() << ": Pos(" << getBpos() << "), Dur(" << getLength() << "), Parts(" << getParts() <<  "), Time(" << getTime() << ")" << std::endl;
  }

  ///\brief Returns the duration of this fragment
  unsigned long Fragment::getDuration() {
    return Bit::btohl(data);
  }

  ///\brief Sets the duration of this fragment
  void Fragment::setDuration(unsigned long newDuration) {
    Bit::htobl(data, newDuration);
  }

  ///\brief Returns the length of this fragment
  char Fragment::getLength() {
    return data[4];
  }

  ///\brief Sets the length of this fragment
  void Fragment::setLength(char newLength) {
    data[4] = newLength;
  }

  ///\brief Returns the number of the first keyframe in this fragment
  unsigned long Fragment::getNumber() {
    return Bit::btohl(data + 5);
  }

  ///\brief Sets the number of the first keyframe in this fragment
  void Fragment::setNumber(unsigned long newNumber) {
    Bit::htobl(data + 5, newNumber);
  }

  ///\brief Returns the size of a fragment
  unsigned long Fragment::getSize() {
    return Bit::btohl(data + 9);
  }

  ///\brief Sets the size of a fragement
  void Fragment::setSize(unsigned long newSize) {
    Bit::htobl(data + 9, newSize);
  }

  ///\brief Returns thte data of this fragment structure
  char * Fragment::getData() {
    return data;
  }

  ///\brief Converts a fragment to a human readable string
  ///\param str The stringstream to append to
  ///\param indent the amount of indentation needed
  void Fragment::toPrettyString(std::ostream & str, int indent) {
    str << std::string(indent, ' ') << "Fragment " << getNumber() << ": Dur(" << getDuration() << "), Len(" << (int)getLength() << "), Size(" << getSize() << ")" << std::endl;
  }

  ///\brief Constructs an empty track
  Track::Track() {
    trackID = 0;
    firstms = 0;
    lastms = 0;
    bps = 0;
    max_bps = 0;
    missedFrags = 0;
    rate = 0;
    size = 0;
    channels = 0;
    width = 0;
    height = 0;
    fpks = 0;
    minKeepAway = 0;
  }

  ///\brief Constructs a track from a JSON::Value
  Track::Track(JSON::Value & trackRef) {
    if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) {
      Fragment * tmp = (Fragment *)trackRef["fragments"].asStringRef().data();
      fragments = std::deque<Fragment>(tmp, tmp + (trackRef["fragments"].asStringRef().size() / PACKED_FRAGMENT_SIZE));
    }
    if (trackRef.isMember("keys") && trackRef["keys"].isString()) {
      Key * tmp = (Key *)trackRef["keys"].asStringRef().data();
      keys = std::deque<Key>(tmp, tmp + (trackRef["keys"].asStringRef().size() / PACKED_KEY_SIZE));
    }
    if (trackRef.isMember("parts") && trackRef["parts"].isString()) {
      Part * tmp = (Part *)trackRef["parts"].asStringRef().data();
      parts = std::deque<Part>(tmp, tmp + (trackRef["parts"].asStringRef().size() / 9));
    }
    trackID = trackRef["trackid"].asInt();
    firstms = trackRef["firstms"].asInt();
    lastms = trackRef["lastms"].asInt();
    bps = trackRef["bps"].asInt();
    max_bps = trackRef["maxbps"].asInt();
    missedFrags = trackRef["missed_frags"].asInt();
    codec = trackRef["codec"].asStringRef();
    type = trackRef["type"].asStringRef();
    init = trackRef["init"].asStringRef();
    if (trackRef.isMember("lang") && trackRef["lang"].asStringRef().size()){
      lang = trackRef["lang"].asStringRef();
    }
    if (type == "audio") {
      rate = trackRef["rate"].asInt();
      size = trackRef["size"].asInt();
      channels = trackRef["channels"].asInt();
    }
    if (type == "video") {
      width = trackRef["width"].asInt();
      height = trackRef["height"].asInt();
      fpks = trackRef["fpks"].asInt();
    }
    if (trackRef.isMember("keysizes") && trackRef["keysizes"].isString()) {
      std::string tmp = trackRef["keysizes"].asStringRef();
      for (unsigned int i = 0; i < tmp.size(); i += 4){
        keySizes.push_back((((long unsigned)tmp[i]) << 24) | (((long unsigned)tmp[i+1]) << 16) | (((long unsigned int)tmp[i+2]) << 8) | tmp[i+3]);
      }
    }
    if (trackRef.isMember("keepaway") && trackRef["keepaway"].isInt()){
      minKeepAway = trackRef["keepaway"].asInt();
    }else{
      minKeepAway = 0;
    }
  }

  ///\brief Constructs a track from a JSON::Value
  Track::Track(Scan & trackRef) {
    if (trackRef.getMember("fragments").getType() == DTSC_STR) {
      char * tmp = 0;
      size_t tmplen = 0;
      trackRef.getMember("fragments").getString(tmp, tmplen);
      fragments = std::deque<Fragment>((Fragment *)tmp, ((Fragment *)tmp) + (tmplen / PACKED_FRAGMENT_SIZE));
    }
    if (trackRef.getMember("keys").getType() == DTSC_STR) {
      char * tmp = 0;
      size_t tmplen = 0;
      trackRef.getMember("keys").getString(tmp, tmplen);
      keys = std::deque<Key>((Key *)tmp, ((Key *)tmp) + (tmplen / PACKED_KEY_SIZE));
    }
    if (trackRef.getMember("parts").getType() == DTSC_STR) {
      char * tmp = 0;
      size_t tmplen = 0;
      trackRef.getMember("parts").getString(tmp, tmplen);
      parts = std::deque<Part>((Part *)tmp, ((Part *)tmp) + (tmplen / 9));
    }
    trackID = trackRef.getMember("trackid").asInt();
    firstms = trackRef.getMember("firstms").asInt();
    lastms = trackRef.getMember("lastms").asInt();
    bps = trackRef.getMember("bps").asInt();
    max_bps = trackRef.getMember("maxbps").asInt();
    missedFrags = trackRef.getMember("missed_frags").asInt();
    codec = trackRef.getMember("codec").asString();
    type = trackRef.getMember("type").asString();
    init = trackRef.getMember("init").asString();
    if (trackRef.getMember("lang")){
      lang = trackRef.getMember("lang").asString();
    }
    if (type == "audio") {
      rate = trackRef.getMember("rate").asInt();
      size = trackRef.getMember("size").asInt();
      channels = trackRef.getMember("channels").asInt();
    }
    if (type == "video") {
      width = trackRef.getMember("width").asInt();
      height = trackRef.getMember("height").asInt();
      fpks = trackRef.getMember("fpks").asInt();
    }
    if (trackRef.getMember("keysizes").getType() == DTSC_STR) {
      char * tmp = 0;
      size_t tmplen = 0;
      trackRef.getMember("keysizes").getString(tmp, tmplen);
      for (unsigned int i = 0; i < tmplen; i += 4){
        keySizes.push_back((((long unsigned)tmp[i]) << 24) | (((long unsigned)tmp[i+1]) << 16) | (((long unsigned int)tmp[i+2]) << 8) | tmp[i+3]);
      }
    }
    if (trackRef.getMember("keepaway").getType() == DTSC_INT){
      minKeepAway = trackRef.getMember("keepaway").asInt();
    }else{
      minKeepAway = 0;
    }
  }

  ///\brief Updates a track and its metadata given new packet properties.
  ///Will also insert keyframes on non-video tracks, and creates fragments
  void Track::update(long long packTime, long long packOffset, long long packDataSize, uint64_t packBytePos, bool isKeyframe, long long packSendSize, unsigned long segment_size) {
    if ((unsigned long long)packTime < lastms) {
      static bool warned = false;
      if (!warned){
        ERROR_MSG("Received packets for track %u in wrong order (%lld < %llu) - ignoring! Further messages on HIGH level.", trackID, packTime, lastms);
        warned = true;
      }else{
        HIGH_MSG("Received packets for track %u in wrong order (%lld < %llu) - ignoring! Further messages on HIGH level.", trackID, packTime, lastms);
      }
      return;
    }
    Part newPart;
    newPart.setSize(packDataSize);
    newPart.setOffset(packOffset);
    if (parts.size()) {
      parts[parts.size() - 1].setDuration(packTime - lastms);
      newPart.setDuration(packTime - lastms);
    } else {
      newPart.setDuration(0);
    }
    parts.push_back(newPart);
    lastms = packTime;
    if (isKeyframe || !keys.size() || (type != "video" && packTime >= AUDIO_KEY_INTERVAL && packTime - (unsigned long long)keys[keys.size() - 1].getTime() >= AUDIO_KEY_INTERVAL)){
      Key newKey;
      newKey.setTime(packTime);
      newKey.setParts(0);
      newKey.setLength(0);
      if (keys.size()) {
        newKey.setNumber(keys[keys.size() - 1].getNumber() + 1);
        keys[keys.size() - 1].setLength(packTime - keys[keys.size() - 1].getTime());
      } else {
        newKey.setNumber(1);
      }
      if (packBytePos) { //For VoD
        newKey.setBpos(packBytePos);
      } else {
        newKey.setBpos(0);
      }
      keys.push_back(newKey);
      keySizes.push_back(0);
      firstms = keys[0].getTime();
      if (!fragments.size() || ((unsigned long long)packTime > segment_size && (unsigned long long)packTime - segment_size >= (unsigned long long)getKey(fragments.rbegin()->getNumber()).getTime())) {
        //new fragment
        Fragment newFrag;
        newFrag.setDuration(0);
        newFrag.setLength(1);
        newFrag.setNumber(keys[keys.size() - 1].getNumber());
        if (fragments.size()) {
          fragments[fragments.size() - 1].setDuration(packTime - getKey(fragments[fragments.size() - 1].getNumber()).getTime());
          uint64_t totalBytes = 0;
          uint64_t totalDuration = 0;
          for (std::deque<DTSC::Fragment>::iterator it = fragments.begin(); it != fragments.end(); it++){
            totalBytes += it->getSize();
            totalDuration += it->getDuration();
          }
          bps = totalDuration ? (totalBytes * 1000) / totalDuration : 0;
          max_bps = std::max(max_bps, (int)((fragments.rbegin()->getSize() * 1000) / fragments.rbegin()->getDuration()));
        }
        newFrag.setDuration(0);
        newFrag.setSize(0);
        fragments.push_back(newFrag);
        //We set the insert time lastms-firstms in the future, to prevent unstable playback
        fragInsertTime.push_back(Util::bootSecs() + ((lastms - firstms)/1000));
      } else {
        Fragment & lastFrag = fragments[fragments.size() - 1];
        lastFrag.setLength(lastFrag.getLength() + 1);
      }
    }
    keys.rbegin()->setParts(keys.rbegin()->getParts() + 1);
    (*keySizes.rbegin()) += packSendSize;
    fragments.rbegin()->setSize(fragments.rbegin()->getSize() + packDataSize);
  }

  void Track::clearParts(){
    while (fragments.size() > 1){removeFirstKey();}
  }

  /// Removes the first buffered key, including any fragments it was part of
  void Track::removeFirstKey(){
    HIGH_MSG("Erasing key %d:%lu", trackID, keys[0].getNumber());
    //remove all parts of this key
    for (int i = 0; i < keys[0].getParts(); i++) {
      parts.pop_front();
    }
    //remove the key itself
    keys.pop_front();
    keySizes.pop_front();
    //update firstms
    firstms = keys[0].getTime();
    //delete any fragments no longer fully buffered
    while (fragments.size() && keys.size() && fragments[0].getNumber() < keys[0].getNumber()) {
      fragments.pop_front();
      fragInsertTime.pop_front();
      //and update the missed fragment counter
      ++missedFrags;
    }
  }

  /// Returns the amount of whole seconds since the first fragment was inserted into the buffer.
  /// This assumes playback from the start of the buffer at time of insert, meaning that
  /// the time is offset by that difference. E.g.: if a buffer is 50s long, the newest fragment
  /// will have a value of 0 until 50s have passed, after which it will increase at a rate of
  /// 1 per second.
  uint32_t Track::secsSinceFirstFragmentInsert(){
    uint32_t bs = Util::bootSecs();
    if (bs > fragInsertTime.front()){
      return bs - fragInsertTime.front();
    }else{
      return 0;
    }
  }
  
  void Track::finalize(){
    keys.rbegin()->setLength(lastms - keys.rbegin()->getTime() + parts.rbegin()->getDuration());
  }

  /// Returns the duration in ms of the longest-duration fragment.
  uint32_t Track::biggestFragment(){
    uint32_t ret = 0;
    for (unsigned int i = 0; i<fragments.size(); i++){
      if (fragments[i].getDuration() > ret){
        ret = fragments[i].getDuration();
      }
    }
    return ret;
  }
  
  ///\brief Returns a key given its number, or an empty key if the number is out of bounds
  Key & Track::getKey(unsigned int keyNum) {
    static Key empty;
    if (!keys.size() || keyNum < keys[0].getNumber()) {
      return empty;
    }
    if ((keyNum - keys[0].getNumber()) > keys.size()) {
      return empty;
    }
    return keys[keyNum - keys[0].getNumber()];
  }

  ///\brief Returns a fragment given its number, or an empty fragment if the number is out of bounds
  Fragment & Track::getFrag(unsigned int fragNum) {
    static Fragment empty;
    if (!fragments.size() || fragNum < fragments[0].getNumber() || fragNum > fragments.rbegin()->getNumber()) {
      return empty;
    }
    for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); ++it){
      if (fragNum >= it->getNumber() && fragNum <= it->getNumber() + it->getLength()){
        return *it;
      }
    }
    return empty;
  }

  /// Returns the number of the key containing timestamp, or last key if nowhere.
  unsigned int Track::timeToKeynum(unsigned int timestamp){
    unsigned int result = 0;
    for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++){
      if (it->getTime() > timestamp){
        break;
      }
      result = it->getNumber();
    }
    return result;
  }

  /// Gets indice of the fragment containing timestamp, or last fragment if nowhere.
  uint32_t Track::timeToFragnum(uint64_t timestamp){
    uint32_t i = 0;
    for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); ++it){
      if (timestamp < getKey(it->getNumber()).getTime() + it->getDuration()){
        return i;
      }
      ++i;
    }
    return fragments.size()-1;
  }

  ///\brief Resets a track, clears all meta values
  void Track::reset() {
    fragments.clear();
    fragInsertTime.clear();
    parts.clear();
    keySizes.clear();
    keys.clear();
    bps = 0;
    max_bps = 0;
    firstms = 0;
    lastms = 0;
  }

  ///\brief Creates an empty meta object
  Meta::Meta() {
    vod = false;
    live = false;
    version = DTSH_VERSION;
    moreheader = 0;
    merged = false;
    bufferWindow = 0;
    bootMsOffset = 0;
  }

  Meta::Meta(const DTSC::Packet & source) {
    reinit(source);
  }

  void Meta::reinit(const DTSC::Packet & source) {
    tracks.clear();
    vod = source.getFlag("vod");
    live = source.getFlag("live");
    version = source.getInt("version");
    merged = source.getFlag("merged");
    bufferWindow = source.getInt("buffer_window");
    moreheader = source.getInt("moreheader");
    bootMsOffset = source.getInt("bootoffset");
    source.getString("source", sourceURI);
    if (source.getScan().hasMember("inputlocalvars")){
      inputLocalVars = source.getScan().getMember("inputlocalvars").asJSON();
    }
    Scan tmpTracks = source.getScan().getMember("tracks");
    unsigned int num = 0;
    Scan tmpTrack;
    do {
      tmpTrack = tmpTracks.getIndice(num);
      if (tmpTrack.asBool()) {
        unsigned int trackId = tmpTrack.getMember("trackid").asInt();
        if (trackId) {
          tracks[trackId] = Track(tmpTrack);
        }
        num++;
      }
    } while (tmpTrack.asBool());
  }

  ///\brief Creates a meta object from a JSON::Value
  Meta::Meta(JSON::Value & meta) {
    vod = meta.isMember("vod") && meta["vod"];
    live = meta.isMember("live") && meta["live"];
    sourceURI = meta.isMember("source") ? meta["source"].asStringRef() : "";
    version = meta.isMember("version") ? meta["version"].asInt() : 0;
    bootMsOffset = meta.isMember("bootoffset") ? meta["bootoffset"].asInt() : 0;
    merged = meta.isMember("merged") && meta["merged"];
    bufferWindow = 0;
    if (meta.isMember("buffer_window")) {
      bufferWindow = meta["buffer_window"].asInt();
    }
    //for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++) {
    jsonForEach(meta["tracks"], it) {
      if ((*it)["trackid"].asInt()) {
        tracks[(*it)["trackid"].asInt()] = Track((*it));
      }
    }
    if (meta.isMember("moreheader")) {
      moreheader = meta["moreheader"].asInt();
    } else {
      moreheader = 0;
    }
  }

  ///\brief Updates a meta object given a JSON::Value
  void Meta::update(JSON::Value & pack, unsigned long segment_size) {
    update(pack["time"].asInt(), pack.isMember("offset")?pack["offset"].asInt():0, pack["trackid"].asInt(), pack["data"].asStringRef().size(), pack.isMember("bpos")?pack["bpos"].asInt():0, pack.isMember("keyframe"), pack.packedSize(), segment_size);
  }

  ///\brief Updates a meta object given a DTSC::Packet
  void Meta::update(const DTSC::Packet & pack, unsigned long segment_size) {
    char * data;
    size_t dataLen;
    pack.getString("data", data, dataLen);
    update(pack.getTime(), pack.hasMember("offset")?pack.getInt("offset"):0, pack.getTrackId(), dataLen, pack.hasMember("bpos")?pack.getInt("bpos"):0, pack.hasMember("keyframe"), pack.getDataLen(), segment_size);
    if (!bootMsOffset && pack.hasMember("bmo")){
      bootMsOffset = pack.getInt("bmo");
    }
  }

  ///\brief Updates a meta object given a DTSC::Packet with byte position override.
  void Meta::updatePosOverride(DTSC::Packet & pack, uint64_t bpos) {
    char * data;
    size_t dataLen;
    pack.getString("data", data, dataLen);
    update(pack.getTime(), pack.hasMember("offset")?pack.getInt("offset"):0, pack.getTrackId(), dataLen, bpos, pack.hasMember("keyframe"), pack.getDataLen());
  }

  void Meta::update(long long packTime, long long packOffset, long long packTrack, long long packDataSize, uint64_t packBytePos, bool isKeyframe, long long packSendSize, unsigned long segment_size){
    DONTEVEN_MSG("Updating meta with: t=%lld, o=%lld, s=%lld, t=%lld, p=%lld", packTime, packOffset, packDataSize, packTrack, packBytePos);
    if (!packSendSize){
      //time and trackID are part of the 20-byte header.
      //the container object adds 4 bytes (plus 2+namelen for each content, see below)
      //offset, if non-zero, adds 9 bytes (integer type) and 8 bytes (2+namelen)
      //bpos, if >= 0, adds 9 bytes (integer type) and 6 bytes (2+namelen)
      //keyframe, if true, adds 9 bytes (integer type) and 10 bytes (2+namelen)
      //data adds packDataSize+5 bytes (string type) and 6 bytes (2+namelen)
      packSendSize = 24 + (packOffset?17:0) + (packBytePos?15:0) + (isKeyframe?19:0) + packDataSize+11;
    }
    if (vod != (packBytePos > 0)){
      INFO_MSG("Changing stream from %s to %s (bPos=%lld)", vod?"VoD":"live", packBytePos?"Vod":"live", packBytePos);
    }
    vod = (packBytePos > 0);
    live = !vod;
    EXTREME_MSG("Updating meta with %lld@%lld+%lld", packTrack, packTime, packOffset);
    if (packTrack > 0 && tracks.count(packTrack)){
      tracks[packTrack].update(packTime, packOffset, packDataSize, packBytePos, isKeyframe, packSendSize, segment_size);
    }
  }

  /// Returns a reference to the first video track, or the first track.
  /// Beware: returns a reference to invalid memory if there are no tracks!
  /// Will print a WARN-level message if this is the case.
  Track & Meta::mainTrack(){
    if (!tracks.size()){
      WARN_MSG("Returning nonsense reference - crashing is likely");
      return tracks.begin()->second;
    }
    for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
      if (it->second.type == "video"){
        return it->second;
      }
    }
    return tracks.begin()->second;
  }

  /// Returns 0 if there are no tracks, otherwise calls mainTrack().biggestFragment().
  uint32_t Meta::biggestFragment(){
    if (!tracks.size()){return 0;}
    return mainTrack().biggestFragment();
  }

  ///\brief Converts a track to a human readable string
  ///\param str The stringstream to append to
  ///\param indent the amount of indentation needed
  ///\param verbosity How verbose the output needs to be
  void Track::toPrettyString(std::ostream & str, int indent, int verbosity) {
    str << std::string(indent, ' ') << "Track " << getWritableIdentifier() << std::endl;
    str << std::string(indent + 2, ' ') << "ID: " << trackID << std::endl;
    str << std::string(indent + 2, ' ') << "Firstms: " << firstms << std::endl;
    str << std::string(indent + 2, ' ') << "Lastms: " << lastms << std::endl;
    str << std::string(indent + 2, ' ') << "Bps: " << bps << std::endl;
    str << std::string(indent + 2, ' ') << "Peak bps: " << max_bps << std::endl;
    if (missedFrags) {
      str << std::string(indent + 2, ' ') << "missedFrags: " << missedFrags << std::endl;
    }
    str << std::string(indent + 2, ' ') << "Codec: " << codec << std::endl;
    str << std::string(indent + 2, ' ') << "Type: " << type << std::endl;
    str << std::string(indent + 2, ' ') << "Init: ";
    for (unsigned int i = 0; i < init.size(); ++i) {
      str << std::hex << std::setw(2) << std::setfill('0') << (int)init[i];
    }
    str << std::dec << std::endl;
    if (lang.size()){
      str << std::string(indent + 2, ' ') << "Language: " << lang << std::endl;
    }
    if (type == "audio") {
      str << std::string(indent + 2, ' ') << "Rate: " << rate << std::endl;
      str << std::string(indent + 2, ' ') << "Size: " << size << std::endl;
      str << std::string(indent + 2, ' ') << "Channel: " << channels << std::endl;
    } else if (type == "video") {
      str << std::string(indent + 2, ' ') << "Width: " << width << std::endl;
      str << std::string(indent + 2, ' ') << "Height: " << height << std::endl;
      str << std::string(indent + 2, ' ') << "Fpks: " << fpks << std::endl;
    }
    str << std::string(indent + 2, ' ') << "Fragments: " << fragments.size() << std::endl;
    if (verbosity & 0x01) {
      for (unsigned int i = 0; i < fragments.size(); i++) {
        fragments[i].toPrettyString(str, indent + 4);
      }
    }
    str << std::string(indent + 2, ' ') << "Keys: " << keys.size() << std::endl;
    if (verbosity & 0x02) {
      for (unsigned int i = 0; i < keys.size(); i++) {
        keys[i].toPrettyString(str, indent + 4);
      }
    }
    str << std::string(indent + 2, ' ') << "KeySizes: " << keySizes.size() << std::endl;
    if (keySizes.size() && verbosity & 0x02){
      for (unsigned int i = 0; i < keySizes.size(); i++){
        str << std::string(indent + 4, ' ') << "[" << i << "] " << keySizes[i] << std::endl;
      }
    }
    str << std::string(indent + 2, ' ') << "Parts: " << parts.size() << std::endl;
    if (verbosity & 0x04) {
      for (unsigned int i = 0; i < parts.size(); i++) {
        parts[i].toPrettyString(str, indent + 4);
      }
    }
  }

  ///\brief Converts a short to a char*
  char * convertShort(short input) {
    static char result[2];
    result[0] = (input >> 8) & 0xFF;
    result[1] = (input) & 0xFF;
    return result;
  }

  ///\brief Converts an integer to a char*
  char * convertInt(int input) {
    static char result[4];
    result[0] = (input >> 24) & 0xFF;
    result[1] = (input >> 16) & 0xFF;
    result[2] = (input >> 8) & 0xFF;
    result[3] = (input) & 0xFF;
    return result;
  }

  ///\brief Converts a long long to a char*
  char * convertLongLong(long long int input) {
    static char result[8];
    result[0] = (input >> 56) & 0xFF;
    result[1] = (input >> 48) & 0xFF;
    result[2] = (input >> 40) & 0xFF;
    result[3] = (input >> 32) & 0xFF;
    result[4] = (input >> 24) & 0xFF;
    result[5] = (input >> 16) & 0xFF;
    result[6] = (input >> 8) & 0xFF;
    result[7] = (input) & 0xFF;
    return result;
  }


  ///\brief Returns a unique identifier for a track
  std::string Track::getIdentifier() {
    std::stringstream result;
    if (type == "") {
      result << "metadata_" << trackID;
      return result.str();
    }else{
      result << type << "_";
    }
    result << codec << "_";
    if (type == "audio") {
      result << channels << "ch_";
      result << rate << "hz";
    } else if (type == "video") {
      result << width << "x" << height << "_";
      result << (double)fpks / 1000 << "fps";
    }
    if (lang.size() && lang != "und"){
      result << "_" << lang;
    }
    return result.str();
  }

  ///\brief Returns a writable identifier for a track, to prevent overwrites on readout
  std::string Track::getWritableIdentifier() {
    if (cachedIdent.size()){return cachedIdent;}
    std::stringstream result;
    result << getIdentifier() << "_" << trackID;
    cachedIdent = result.str();
    return cachedIdent;
  }

  ///\brief Determines the "packed" size of a track
  int Track::getSendLen(bool skipDynamic) {
    int result = 124 + init.size() + codec.size() + type.size() + getWritableIdentifier().size();
    if (!skipDynamic){
      result += fragments.size() * PACKED_FRAGMENT_SIZE + 16;
      result += keys.size() * PACKED_KEY_SIZE + 11;
      result += (keySizes.size() * 4) + 15;
      result += parts.size() * 9 + 12;
    }
    if (lang.size() && lang != "und"){
      result += 11 + lang.size();
    }
    if (type == "audio") {
      result += 49;
    } else if (type == "video") {
      result += 48;
    }
    if (!skipDynamic && missedFrags) {
      result += 23;
    }
    if (minKeepAway){
      result += 19;
    }
    return result;
  }

  ///\brief Writes a pointer to the specified destination
  ///
  ///Does a memcpy and increases the destination pointer accordingly
  static void writePointer(char *& p, const char * src, unsigned int len) {
    memcpy(p, src, len);
    p += len;
  }

  ///\brief Writes a pointer to the specified destination
  ///
  ///Does a memcpy and increases the destination pointer accordingly
  static void writePointer(char *& p, const std::string & src) {
    writePointer(p, src.data(), src.size());
  }

  ///\brief Writes a track to a pointer
  void Track::writeTo(char *& p) {
    std::deque<Fragment>::iterator firstFrag = fragments.begin(); 
    if (fragments.size() && (&firstFrag) == 0){
      return;
    }
    std::string trackIdent = getWritableIdentifier();
    writePointer(p, convertShort(trackIdent.size()), 2);
    writePointer(p, trackIdent);
    writePointer(p, "\340", 1);//Begin track object
    writePointer(p, "\000\011fragments\002", 12);
    writePointer(p, convertInt(fragments.size() * PACKED_FRAGMENT_SIZE), 4);
    for (; firstFrag != fragments.end(); ++firstFrag) {
      writePointer(p, firstFrag->getData(), PACKED_FRAGMENT_SIZE);
    }
    writePointer(p, "\000\004keys\002", 7);
    writePointer(p, convertInt(keys.size() * PACKED_KEY_SIZE), 4);
    for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++) {
      writePointer(p, it->getData(), PACKED_KEY_SIZE);
    }
    writePointer(p, "\000\010keysizes\002,", 11);
    writePointer(p, convertInt(keySizes.size() * 4), 4);
    std::string tmp;
    tmp.reserve(keySizes.size() * 4);
    for (unsigned int i = 0; i < keySizes.size(); i++){
      tmp += (char)(keySizes[i] >> 24);
      tmp += (char)(keySizes[i] >> 16);
      tmp += (char)(keySizes[i] >> 8);
      tmp += (char)(keySizes[i]);
    }
    writePointer(p, tmp.data(), tmp.size());
    writePointer(p, "\000\005parts\002", 8);
    writePointer(p, convertInt(parts.size() * 9), 4);
    for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
      writePointer(p, it->getData(), 9);
    }
    writePointer(p, "\000\007trackid\001", 10);
    writePointer(p, convertLongLong(trackID), 8);
    if (missedFrags) {
      writePointer(p, "\000\014missed_frags\001", 15);
      writePointer(p, convertLongLong(missedFrags), 8);
    }
    writePointer(p, "\000\007firstms\001", 10);
    writePointer(p, convertLongLong(firstms), 8);
    writePointer(p, "\000\006lastms\001", 9);
    writePointer(p, convertLongLong(lastms), 8);
    writePointer(p, "\000\003bps\001", 6);
    writePointer(p, convertLongLong(bps), 8);
    writePointer(p, "\000\006maxbps\001", 9);
    writePointer(p, convertLongLong(max_bps), 8);
    writePointer(p, "\000\004init\002", 7);
    writePointer(p, convertInt(init.size()), 4);
    writePointer(p, init);
    writePointer(p, "\000\005codec\002", 8);
    writePointer(p, convertInt(codec.size()), 4);
    writePointer(p, codec);
    writePointer(p, "\000\004type\002", 7);
    writePointer(p, convertInt(type.size()), 4);
    writePointer(p, type);
    if (lang.size() && lang != "und"){
      writePointer(p, "\000\004lang\002", 7);
      writePointer(p, convertInt(lang.size()), 4);
      writePointer(p, lang);
    }
    if (type == "audio") {
      writePointer(p, "\000\004rate\001", 7);
      writePointer(p, convertLongLong(rate), 8);
      writePointer(p, "\000\004size\001", 7);
      writePointer(p, convertLongLong(size), 8);
      writePointer(p, "\000\010channels\001", 11);
      writePointer(p, convertLongLong(channels), 8);
    } else if (type == "video") {
      writePointer(p, "\000\005width\001", 8);
      writePointer(p, convertLongLong(width), 8);
      writePointer(p, "\000\006height\001", 9);
      writePointer(p, convertLongLong(height), 8);
      writePointer(p, "\000\004fpks\001", 7);
      writePointer(p, convertLongLong(fpks), 8);
    }
    if (minKeepAway){
      writePointer(p, "\000\010keepaway\001", 11);
      writePointer(p, convertLongLong(minKeepAway), 8);
    }
    writePointer(p, "\000\000\356", 3);//End this track Object
  }

  ///\brief Writes a track to a socket
  void Track::send(Socket::Connection & conn, bool skipDynamic) {
    conn.SendNow(convertShort(getWritableIdentifier().size()), 2);
    conn.SendNow(getWritableIdentifier());
    conn.SendNow("\340", 1);//Begin track object
    if (!skipDynamic){
    conn.SendNow("\000\011fragments\002", 12);
      conn.SendNow(convertInt(fragments.size() * PACKED_FRAGMENT_SIZE), 4);
    for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); it++) {
        conn.SendNow(it->getData(), PACKED_FRAGMENT_SIZE);
    }
    conn.SendNow("\000\004keys\002", 7);
      conn.SendNow(convertInt(keys.size() * PACKED_KEY_SIZE), 4);
    for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++) {
        conn.SendNow(it->getData(), PACKED_KEY_SIZE);
    }
    conn.SendNow("\000\010keysizes\002,", 11);
    conn.SendNow(convertInt(keySizes.size() * 4), 4);
    std::string tmp;
    tmp.reserve(keySizes.size() * 4);
    for (unsigned int i = 0; i < keySizes.size(); i++){
      tmp += (char)(keySizes[i] >> 24);
      tmp += (char)(keySizes[i] >> 16);
      tmp += (char)(keySizes[i] >> 8);
      tmp += (char)(keySizes[i]);
    }
    conn.SendNow(tmp.data(), tmp.size());
    conn.SendNow("\000\005parts\002", 8);
    conn.SendNow(convertInt(parts.size() * 9), 4);
    for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
      conn.SendNow(it->getData(), 9);
    }
    }
    conn.SendNow("\000\007trackid\001", 10);
    conn.SendNow(convertLongLong(trackID), 8);
    if (!skipDynamic && missedFrags) {
      conn.SendNow("\000\014missed_frags\001", 15);
      conn.SendNow(convertLongLong(missedFrags), 8);
    }
    conn.SendNow("\000\007firstms\001", 10);
    conn.SendNow(convertLongLong(firstms), 8);
    conn.SendNow("\000\006lastms\001", 9);
    conn.SendNow(convertLongLong(lastms), 8);
    conn.SendNow("\000\003bps\001", 6);
    conn.SendNow(convertLongLong(bps), 8);
    conn.SendNow("\000\006maxbps\001", 9);
    conn.SendNow(convertLongLong(max_bps), 8);
    conn.SendNow("\000\004init\002", 7);
    conn.SendNow(convertInt(init.size()), 4);
    conn.SendNow(init);
    conn.SendNow("\000\005codec\002", 8);
    conn.SendNow(convertInt(codec.size()), 4);
    conn.SendNow(codec);
    conn.SendNow("\000\004type\002", 7);
    conn.SendNow(convertInt(type.size()), 4);
    conn.SendNow(type);
    if (lang.size() && lang != "und"){
    conn.SendNow("\000\004lang\002", 7);
    conn.SendNow(convertInt(lang.size()), 4);
    conn.SendNow(lang);
    }
    if (type == "audio") {
      conn.SendNow("\000\004rate\001", 7);
      conn.SendNow(convertLongLong(rate), 8);
      conn.SendNow("\000\004size\001", 7);
      conn.SendNow(convertLongLong(size), 8);
      conn.SendNow("\000\010channels\001", 11);
      conn.SendNow(convertLongLong(channels), 8);
    } else if (type == "video") {
      conn.SendNow("\000\005width\001", 8);
      conn.SendNow(convertLongLong(width), 8);
      conn.SendNow("\000\006height\001", 9);
      conn.SendNow(convertLongLong(height), 8);
      conn.SendNow("\000\004fpks\001", 7);
      conn.SendNow(convertLongLong(fpks), 8);
    }
    if (minKeepAway){
      conn.SendNow("\000\010keepaway\001", 11);
      conn.SendNow(convertLongLong(minKeepAway), 8);
    }
    conn.SendNow("\000\000\356", 3);//End this track Object
  }

  ///\brief Determines the "packed" size of a meta object
  unsigned int Meta::getSendLen(bool skipDynamic, std::set<unsigned long> selectedTracks) {
    unsigned int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21;
    for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
      if (!selectedTracks.size() || selectedTracks.count(it->first)){
        dataLen += it->second.getSendLen(skipDynamic);
      }
    }
    if (version){dataLen += 18;}
    if (bootMsOffset){dataLen += 21;}
    if (sourceURI.size()){dataLen += 13+sourceURI.size();}
    return dataLen + 8; //add 8 bytes header
  }

  ///\brief Writes a meta object to a pointer
  void Meta::writeTo(char * p) {
    int dataLen = getSendLen() - 8; //strip 8 bytes header
    writePointer(p, DTSC::Magic_Header, 4);
    writePointer(p, convertInt(dataLen), 4);
    writePointer(p, "\340\000\006tracks\340", 10);
    for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
      it->second.writeTo(p);
    }
    writePointer(p, "\000\000\356", 3);//End tracks object
    if (vod) {
      writePointer(p, "\000\003vod\001", 6);
      writePointer(p, convertLongLong(1), 8);
    }
    if (live) {
      writePointer(p, "\000\004live\001", 7);
      writePointer(p, convertLongLong(1), 8);
    }
    if (merged) {
      writePointer(p, "\000\006merged\001", 9);
      writePointer(p, convertLongLong(1), 8);
    }
    if (version) {
      writePointer(p, "\000\007version\001", 10);
      writePointer(p, convertLongLong(version), 8);
    }
    if (bootMsOffset) {
      writePointer(p, "\000\012bootoffset\001", 13);
      writePointer(p, convertLongLong(bootMsOffset), 8);
    }
    if (sourceURI.size()) {
      writePointer(p, "\000\006source\002", 9);
      writePointer(p, convertInt(sourceURI.size()), 4);
      writePointer(p, sourceURI);
    }
    if (bufferWindow) {
      writePointer(p, "\000\015buffer_window\001", 16);
      writePointer(p, convertLongLong(bufferWindow), 8);
    }
    writePointer(p, "\000\012moreheader\001", 13);
    writePointer(p, convertLongLong(moreheader), 8);
    writePointer(p, "\000\000\356", 3);//End global object
  }

  ///\brief Writes a meta object to a socket
  void Meta::send(Socket::Connection & conn, bool skipDynamic, std::set<unsigned long> selectedTracks) {
    int dataLen = getSendLen(skipDynamic, selectedTracks) - 8; //strip 8 bytes header
    conn.SendNow(DTSC::Magic_Header, 4);
    conn.SendNow(convertInt(dataLen), 4);
    conn.SendNow("\340\000\006tracks\340", 10);
    for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
      if (!selectedTracks.size() || selectedTracks.count(it->first)){
        it->second.send(conn, skipDynamic);
      }
    }
    conn.SendNow("\000\000\356", 3);//End tracks object
    if (vod) {
      conn.SendNow("\000\003vod\001", 6);
      conn.SendNow(convertLongLong(1), 8);
    }
    if (live) {
      conn.SendNow("\000\004live\001", 7);
      conn.SendNow(convertLongLong(1), 8);
    }
    if (merged) {
      conn.SendNow("\000\006merged\001", 9);
      conn.SendNow(convertLongLong(1), 8);
    }
    if (version) {
      conn.SendNow("\000\007version\001", 10);
      conn.SendNow(convertLongLong(version), 8);
    }
    if (bootMsOffset) {
      conn.SendNow("\000\012bootoffset\001", 13);
      conn.SendNow(convertLongLong(bootMsOffset), 8);
    }
    if (sourceURI.size()) {
      conn.SendNow("\000\006source\002", 9);
      conn.SendNow(convertInt(sourceURI.size()), 4);
      conn.SendNow(sourceURI);
    }
    if (bufferWindow) {
      conn.SendNow("\000\015buffer_window\001", 16);
      conn.SendNow(convertLongLong(bufferWindow), 8);
    }
    conn.SendNow("\000\012moreheader\001", 13);
    conn.SendNow(convertLongLong(moreheader), 8);
    conn.SendNow("\000\000\356", 3);//End global object
  }

  ///\brief Converts a track to a JSON::Value
  JSON::Value Track::toJSON(bool skipDynamic) {
    JSON::Value result;
    std::string tmp;
    if (!skipDynamic) {
      tmp.reserve(fragments.size() * PACKED_FRAGMENT_SIZE);
      for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); it++) {
        tmp.append(it->getData(), PACKED_FRAGMENT_SIZE);
      }
      result["fragments"] = tmp;
      tmp = "";
      tmp.reserve(keys.size() * PACKED_KEY_SIZE);
      for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++) {
        tmp.append(it->getData(), PACKED_KEY_SIZE);
      }
      result["keys"] = tmp;
      tmp = "";
      tmp.reserve(keySizes.size() * 4);
      for (unsigned int i = 0; i < keySizes.size(); i++){
        tmp += (char)((keySizes[i] >> 24));
        tmp += (char)((keySizes[i] >> 16));
        tmp += (char)((keySizes[i] >> 8));
        tmp += (char)(keySizes[i]);
      }
      result["keysizes"] = tmp;
      tmp = "";
      tmp.reserve(parts.size() * 9);
      for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
        tmp.append(it->getData(), 9);
      }
      result["parts"] = tmp;
    }
    result["init"] = init;
    if (lang.size() && lang != "und"){
      result["lang"] = lang;
    }
    result["trackid"] = trackID;
    result["firstms"] = firstms;
    result["lastms"] = lastms;
    result["bps"] = bps;
    result["maxbps"] = max_bps;
    if (missedFrags) {
      result["missed_frags"] = missedFrags;
    }
    result["codec"] = codec;
    result["type"] = type;
    if (type == "audio") {
      result["rate"] = rate;
      result["size"] = size;
      result["channels"] = channels;
    } else if (type == "video") {
      result["width"] = width;
      result["height"] = height;
      result["fpks"] = fpks;
    }

    if(minKeepAway){
      result["keepaway"] = minKeepAway;
    }

    return result;
  }

  ///\brief Converts a meta object to a JSON::Value
  JSON::Value Meta::toJSON() {
    JSON::Value result;
    for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
      result["tracks"][it->second.getWritableIdentifier()] = it->second.toJSON();
    }
    if (vod) {
      result["vod"] = 1;
    }
    if (live) {
      result["live"] = 1;
    }
    if (merged) {
      result["merged"] = 1;
    }
    if (bufferWindow) {
      result["buffer_window"] = bufferWindow;
    }
    if (version) {
      result["version"] = version;
    }
    if (bootMsOffset){
      result["bootoffset"] = bootMsOffset;
    }
    if (sourceURI.size()){
      result["source"] = sourceURI;
    }
    result["moreheader"] = moreheader;
    if (inputLocalVars){
      result["inputlocalvars"] = inputLocalVars;
    }
    return result;
  }

  ///\brief Writes metadata to a filename. Wipes existing contents, if any.
  bool Meta::toFile(const std::string & fileName){
    std::ofstream oFile(fileName.c_str());
    oFile << toJSON().toNetPacked();
    if (!oFile.good()){return false;}
    oFile.close();
    return true;
  }

  ///\brief Converts a meta object to a human readable string
  ///\param str The stringstream to append to
  ///\param indent the amount of indentation needed
  ///\param verbosity How verbose the output needs to be
  void Meta::toPrettyString(std::ostream & str, int indent, int verbosity) {
    for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
      it->second.toPrettyString(str, indent, verbosity);
    }
    if (vod) {
      str << std::string(indent, ' ') << "Video on Demand" << std::endl;
    }
    if (live) {
      str << std::string(indent, ' ') << "Live" << std::endl;
    }
    if (merged) {
      str << std::string(indent, ' ') << "Merged file" << std::endl;
    }
    if (bufferWindow) {
      str << std::string(indent, ' ') << "Buffer Window: " << bufferWindow << std::endl;
    }
    if (sourceURI.size()){
      str << std::string(indent, ' ') << "Source: " << sourceURI << std::endl;
    }
    if (bootMsOffset){
      str << std::string(indent, ' ') << "Boot MS offset: " << bootMsOffset << std::endl;
    }
    str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl;
  }

  ///\brief Resets a meta object, removes all unimportant meta values
  void Meta::reset() {
    for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
      it->second.reset();
    }
    bootMsOffset = 0;
  }


  PartIter::PartIter(Track & Trk, Fragment & frag){
    tRef = &Trk;
    pIt = tRef->parts.begin();
    kIt = tRef->keys.begin();
    uint32_t fragNum = frag.getNumber();
    while (kIt->getNumber() < fragNum && kIt != tRef->keys.end()){
      uint32_t kParts = kIt->getParts();
      for (uint32_t pCount = 0; pCount < kParts && pIt != tRef->parts.end(); ++pCount){
        ++pIt;
      }
      ++kIt;
    }
    if (kIt == tRef->keys.end()){tRef = 0;}
    currInKey = 0;
    lastKey = fragNum + frag.getLength();
  }

  /// Dereferences into a Value reference.
  /// If invalid iterator, returns an empty reference and prints a warning message.
  Part & PartIter::operator*() const{
    if (tRef && pIt != tRef->parts.end()){
      return *pIt;
    }
    static Part error;
    WARN_MSG("Dereferenced invalid Part iterator");
    return error;
  }

  /// Dereferences into a Value reference.
  /// If invalid iterator, returns an empty reference and prints a warning message.
  Part* PartIter::operator->() const{
    return &(operator*());
  }

  /// True if not done iterating.
  PartIter::operator bool() const{
    return (tRef && pIt != tRef->parts.end());
  }

  PartIter & PartIter::operator++(){
    if (*this){
      ++pIt;
      if (++currInKey >= kIt->getParts()){
        currInKey = 0;
        //check if we're done iterating - we assume done if past the last key or arrived past the fragment
        if (++kIt == tRef->keys.end() || kIt->getNumber() >= lastKey){
          tRef = 0;
        }
      }
    }
    return *this;
  }



}



